
package net.lab0.electron.map;


import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;


/**
 * Classe de la racine du quad tree. Les opérations d'implémentation de
 * <code>Collection<QuadTreeNode></code> concernent les QuadTreeNode sauf
 * mention contraire
 * 
 * @author 116
 * 
 */
public class QuadTreeRoot<E extends My2DPointInterface>
implements Collection<QuadTreeNode<E>>
{
	protected QuadTreeNode<E>	rootNode;
	protected int				maxNodeDepth;
	protected int				maxElementsPerNode;
	protected Point2D.Double	maxPoint;
	protected Point2D.Double	minPoint;
	
	public QuadTreeRoot(Collection<E> elements, int maxElementsPerNode, int maxNodeDepth, Point2D.Double p1,
	Point2D.Double p2)
	{
		this.maxElementsPerNode = maxElementsPerNode;
		this.maxNodeDepth = maxNodeDepth;
		this.maxPoint = new Point2D.Double(Math.max(p1.x, p2.x), Math.max(p1.y, p2.y));
		this.minPoint = new Point2D.Double(Math.min(p1.x, p2.x), Math.min(p1.y, p2.y));
		rootNode = new QuadTreeNode<E>(this, new Rectangle2D.Double(minPoint.x, maxPoint.y, maxPoint.x - minPoint.x,
		maxPoint.y - minPoint.y));
	}
	
	public QuadTreeRoot(int maxElementsPerNode, int maxNodeDepth, Point2D.Double p1, Point2D.Double p2)
	{
		this.maxElementsPerNode = maxElementsPerNode;
		this.maxNodeDepth = maxNodeDepth;
		this.maxPoint = new Point2D.Double(Math.max(p1.x, p2.x), Math.max(p1.y, p2.y));
		this.minPoint = new Point2D.Double(Math.min(p1.x, p2.x), Math.min(p1.y, p2.y));
		rootNode = new QuadTreeNode<E>(this, new Rectangle2D.Double(minPoint.x, maxPoint.y, maxPoint.x - minPoint.x,
		maxPoint.y - minPoint.y));
	}
	
	public void addElements(Collection<E> elements)
	throws IllegalArgumentException
	{
		// TODO : expand zone on element add
		Point2D.Double min = min(elements);
		Point2D.Double max = max(elements);
		
		if (min.x < minPoint.x || min.y < minPoint.y || max.x > maxPoint.x || max.y > maxPoint.y)
		{
			throw new IllegalArgumentException();
		}
		
		rootNode.addAll(elements);
	}
	
	private Point2D.Double max(Collection<E> points)
	{
		double xMax = points.iterator().next().getX();
		double yMax = points.iterator().next().getY();
		for (E e : points)
		{
			if (e.getX() > xMax)
			{
				xMax = e.getX();
			}
			if (e.getY() > yMax)
			{
				yMax = e.getY();
			}
		}
		
		return new Point2D.Double(xMax, yMax);
	}
	
	private Point2D.Double min(Collection<E> points)
	{
		// System.out.println(points.size());
		double xMin = points.iterator().next().getX();
		double yMin = points.iterator().next().getY();
		for (E e : points)
		{
			if (e.getX() < xMin)
			{
				xMin = e.getX();
			}
			if (e.getY() < yMin)
			{
				yMin = e.getY();
			}
		}
		
		return new Point2D.Double(xMin, yMin);
	}
	
	public ArrayList<E> getElementsInRectangle(Rectangle2D.Double rect)
	{
		ArrayList<E> list = new ArrayList<E>();
		rootNode.getPointsInRectangle(rect, list);
		return list;
	}
	
	@Override
	public Iterator<QuadTreeNode<E>> iterator()
	{
		return rootNode.getAllNodes().iterator();
	}
	
	/**
	 * 
	 * @return Le nombre de <code>T</code> de l'arbre. Pour connaître le nombre
	 *         de nodes, utiliser <code>size()</code>.
	 */
	public int elementsCount()
	{
		return rootNode.size();
	}
	
	/**
	 * 
	 * @return Le nombre de nodes de l'arbre. Pour connaître le nombre
	 *         d'éléments, utiliser <code>mapElementCount()</code>.
	 */
	@Override
	public int size()
	{
		return rootNode.countNodes();
	}
	
	/**
	 * @return <code>true</code> si il existe au moins une node dans l'arbre.
	 *         False sinon.
	 */
	@Override
	public boolean isEmpty()
	{
		return (rootNode == null); // ne peut pas être vide dans d'autres
									// conditions
	}
	
	@Override
	public boolean contains(Object o)
	{
		if (o == null)
		{
			return false;
		}
		else if (!(o instanceof QuadTreeNode))
		{
			return false;
		}
		else
		{
			return rootNode.getAllNodes().contains(o);
		}
	}
	
	@Override
	public Object[] toArray()
	{
		return rootNode.getAllNodes().toArray();
	}
	
	/**
	 * do nothing
	 * 
	 * @return <code>null</code>
	 */
	@Override
	public <T> T[] toArray(T[] a)
	{
		// TODO Auto-generated method stub
		return null;
	}
	
	/**
	 * Ajoute les éléments du quad tree <code>e</code> au quad tree courrant
	 */
	@Override
	public boolean add(QuadTreeNode<E> e)
	{
		return rootNode.addAll(e.getAllMapElements());
	}
	
	/**
	 * non sens. ne fait rien.
	 */
	@Override
	public boolean remove(Object o)
	{
		// TODO Auto-generated method stub
		return false;
	}
	
	@Override
	public boolean containsAll(Collection<?> c)
	{
		return rootNode.getAllNodes().containsAll(c);
	}
	
	@Override
	public boolean addAll(Collection<? extends QuadTreeNode<E>> c)
	{
		boolean retour = false;
		for (QuadTreeNode<E> node : c)
		{
			retour |= add(node);
		}
		return retour;
	}
	
	/**
	 * non sens. ne fait rien.
	 */
	@Override
	public boolean removeAll(Collection<?> c)
	{
		// TODO Auto-generated method stub
		return false;
	}
	
	/**
	 * non sens. ne fait rien.
	 */
	@Override
	public boolean retainAll(Collection<?> c)
	{
		// TODO Auto-generated method stub
		return false;
	}
	
	@Override
	public void clear()
	{
		rootNode.clear();
	}
	
	public void addElement(E element)
	{
		rootNode.add(element);
	}
	
}
